网上关于Typescript的书实在是太少了,搜来搜去好像就《learning Typescript中文版》一本,果断购买之。读了以后发现,关于Typescript语法的部分其实讲的并不多,更多的是前端项目的架构部分讲解。
暂将本书关于typescript的部分做一些整理。
概念 
Typescript通过向Javascript增加可选的静态类型声明来把Javscript变成强类型的程序语言 
可选的静态类型声明可约束函数,变量,属性等程序实体,这样编译器和相应的开发工具就可以在开发过程中提供更好的正确性验证和智能感知 
 
一. 类型系统 1. 静态类型声明与静态类型推导 1 2 3 4 5 6 7 var  counter = 0 ;	counter = 'asd' ;	 let  a:any = 0 ;a = '你好啊' ;	 var  count;	
 
2. 基本类型 基本类型有boolean,number,string,array ,void和所有用户自定义的enum类型。所有这些类型在Typescript中 ,都是一个唯一的顶层的Any Type类型的子类型,any关键字代表这种类型。
在Typescript中,我们不能把null或undefined当作类型使用
2.1 Array类型的声明 array类型的声明有两种写法。一是可以在数组元素的类型后面跟着[]来表示包含这种类型元素的数组1 var  list:number [] = [1 ,2 ,3 ];
第二种是使用范型数组类型Array1 var  list:Array <number > = [1 ,2 ,3 ]
如果数组里混合了各种类型1 2 3 4 5 var  list:any [] = [1 ,'asd' ,true ];list[1 ] = 100  var  list:Array <number |string |boolean > = [1 ,'asd' ,true ];
2.2 enum类型的声明 enum类型是为了给一个数字集合更友好的命名。enum类型中的成员默认从0开始,但你也可以手动设置成员中的值来更改这种默认行为。1 2 enum  Color {Red,Green,Blue}var  c:Color =Color.Green
会编译为如下代码1 2 3 4 5 6 7 8 9 var  Color;(function  (Color )  {     Color[Color["Red" ] = 0 ] = "Red" ;     Color[Color["Green" ] = 1 ] = "Green" ;     Color[Color["Blue" ] = 2 ] = "Blue" ; })(Color || (Color = {})); var  c = Color.Green;
 
2.3 void类型 从某种程度上,any的对立面就是void,即所有类型都不存在的时候。你会在一个函数没有返回值的时候看到它。 1 2 3 function  warnUser :void ( ) {	alert('asd' ); } 
 
2.4 联合类型 Typescript允许联合类型1 2 3 4 var  path:string []|string ;path = '/temp'  path = ['/temp' ,'/temp1' ]; path = 1  	 
2.5 类型别名 Typescript允许使用type关键字声明类型别名1 2 3 type  PrimitiveArray = Array <string |number |boolean >;type  myNumber = number ;type  CallBack = () =>void ;
2.6 环境声明 环境声明允许在Typescript中创建一个不会被编译到Javascript中的变量。 
1 customerConsole.log('asd' );  
 
1 2 3 4 5 6 interface  ICustomerConsle{    log(arg:string ):void  } declare  var  customeConsole:ICustomerConsle;customeConsole.log('asd' ) 
 
二. 函数 1. 函数的类型 下面是声明函数的参数以及返回值的类型,这种是推荐的写法,比较简洁。
1 2 3 function  getName (name:string  ):string  {    return  "Hi" +name; } 
 
而函数本身的类型也是可以定义的。函数类型的定义即()=>string这种
1 2 3 4 var  getName :(name:string  )=> string ;getName = function (name:string  ):string  { } 
 
interface也可以修饰函数的类型1 2 3 4 5 6 7 8 9 10 11 12 interface  SearchFunc{    (source:string ,subString:string ):boolean  } var  mySearch : SearchFunc = function (source:string ,subString:string  ) {    let  result = source.search(subString);     if (result != -1 ){         return  true ;     }else {         return  false ;     } } 
但是函数的类型声名是可以从被赋值的函数中推断出来的,因此对函数本身使用类型声明不是一个好的实践。
2. 有剩余参数的函数 1 2 3 function  add (num1:number,num2:number,num3:number ):number  {    return  num1 + num2 + num3; } 
 
一个剩余参数必须包含一个数组类型
1 2 3 4 5 6 7 function  add (...foo:Array<number> ) {    let  result = 0 ;     for (var  i=0 ;i<foo.lenth;i++){         result += i;     }     return  result; } 
 
3. 函数重载 
实现签名必须兼容所有的重载签名 
所有重载签名相互之间必须兼容。如果一个函数试图返回一个数字,而另一个试图返回一个数字,则会返回编译错误。 
暂时没有想到应用场景。可以用联合类型来替代。 
 
1 2 3 4 5 6 function  test (name:string  ):string  ;	function  test (name:boolean  ):string  ;	function  test (value:string |boolean  ) {	    return  ''  } 
 
特定重载签名 1 2 3 4 5 6 7 8 9 interface  Document{    createElement(tagName:'div' ):HTMLDivElement     createElement(tagName:'span' ):HTMLSpanElement     createElement(tagName:string ):HTMLElement } let  aaa:Documentlet  oSpan:HTMLSpanElement = aaa.createElement('span' )let  oP:HTMLDivElement = aaa.createElement('span' )	
 
4. 泛型 泛型编程是一种编程语言的风格。它允许程序员使用之后才会定义的类型。
在函数名后面增加了一对角括号(<>)表示这是一个泛型函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function  getEntities <T >(    url:string,     cb:(list:T[] )=>void ):void  {    $.ajax({         url : url,         method : 'GET' ,         success : function (data ) {             cd(data.items)         }     }) } getEntities<User>('/api/users' ,function (users:User[] ) {     for (var  i=0 ;i<users.length;i++){         console .log(users[i].name)     } }) getEntities<Order>('/api/orders' ,function (orders:Orders[] ) {     for (var  i=0 ;i<users.length;i++){         console .log(users[i].name)     } }) 
 
三. Typescript 中的面向对象编程 1. SOLID原则 
单一职责原则(SRP):软件组件(函数,类,模块)必须专注于单一的任务 
开闭原则(OCP): 对拓展开放,对修改封闭 
里式替换原则(LSP) : 只要继承的是同一个接口,程序里任何一个类都可以被其他类替换。替换完成后,不需要其他额外的工作,程序就能像原来一样运行。 
接口隔离原则(ISP):我们应该将那些非常大的接口(大而全的接口)拆分成一些小而具体的接口(特定客户端接口)。 
依赖反转原则(DIP):表明一个方法应该遵从依赖于抽象(接口)而不是一个实例(类)的概念。 
 
1.1 单一职责原则(SRP)原则举例 未遵从,因为增加了一个与Person类无关的validateEmail方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  Person{    public  name :string ;     public  age :number ;     public  email : string ;     constructor (name:string ,age:number ,email:string  ){         this .name = name;         this .age = age;         if (this .validateEmail(email)){             this .email = email;         }else {             throw  new  Error ('Invalid Email' )         }     }     validateEmail(email:string ){         return  /^\S+@\S+\.\S+$/ .test(email)     } } let  zhangsan = new  Person('张三' ,13 ,'fff@fang.com' )
 
遵从
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class  Email{    public  email : string ;     constructor (email:string  ){         if (this .validateEmail(email)){             this .email = email;         }else {             throw  new  Error ('Invalid Email' )         }              }     validateEmail(email:string ){         return  /^\S+@\S+\.\S+$/ .test(email)     } } class  Person{    public  name :string ;     public  age :number ;     public  email : Email;     constructor (name:string ,age:number ,email:Email ){         this .name = name;         this .age = age;         this .email = email;     } } let  zhangsan = new  Person('张三' ,13 ,new  Email('fff@fang.com' ))
 
1.2 里式替换原则(LSP) 举例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 interface  PersistanceServiceInterface{    save(entity:any ):number  } class  CookiePersistanceService implements  PersistanceServiceInterface{    save(entity:any ):number {         var  id = Math .random();                  return  id     } } class  LocalStoragePersistanceService implements  PersistanceServiceInterface{    save(entity:any ):number {         var  id = Math .random();                  return  id     } } class  FavouritesController{    private  _persistanceService : PersistanceService     constructor (persistanceService:PersistanceService ){         this ._persistanceService = persistanceServicel     }     public  saveAsFavourite(articleId : number ){         return  this ._persistanceService.save(articleId)     } } new  FavouritesController(new  CookiePersistanceService())new  FavouritesController(new  LocalStoragePersistanceService())
 
2. 接口(interface) 接口的作用是抽象 。本质就是抽象,让我们在写代码之前对所写的东西有个概念。
在大型软件开发时尤为重要,一个系统模块可以抽象的看做一个 TypeScript 定义的接口。
用带清晰接口的模块来结构化大型系统,这是一种更为抽象的设计形式。接口设计(讨论)与最终实现方式无关,对接口思考得越抽象越有利。换句话说就是让设计脱离实现,最终体现出一种 IDL(接口定义语言,Interface Define Language),让程序设计回归本质。
看个例子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface  Avatar {  cdnUrl: string ;    filePath: string ;    fileSize: number ;  } interface  UserProfile {  cuid?: string ;    avatar?: Avatar;    name: string ;    gender: string ;    age: number ;  } interface  UserModel {  createUser(profile: UserProfile): string ;    getUser(cuid: string ): UserProfile;    listFollowers(cuid: string ): UserProfile[];    followByCuid(cuid: string , who: string ): string ;  } 
 
那我实现上述 Interface 也只需如下进行。
1 2 3 4 5 6 class  UserModelImpl  implements  UserModel   {   createUser(profile: UserProfile): string {          }     } 
 
修饰对象 1 2 3 4 5 6 interface Obj{     name : string;     age : number } let  obj:Obj = {name  : 1 ,age  : 2 }
 
修饰函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface SearchFunc{     (source:string,subString:string):boolean } var mySearch : SearchFunc = function(source:string,subString:string){     let result = source.search(subString);     if(result != -1){         return true;     }else{         return false;     } } //另一种修饰函数的方法 var myFunc:(a:number)=>string = function(a:number):string{return 'Hello'} 
 
修饰数组 1 2 3 4 5 interface StringArray{     [index:number]:string;   } var  myArray:StringArray = ["ime" ,"lily" ];
 
修饰类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface ClockInterface{     currentTime : Date;     setTime(d:Date):string }   class Clock implements ClockInterface{     currentTime : Date;     setTime(d:Date){         this.currentTime = d;         return 'ready';     }     constructor(h:number,m:number){              } } 
 
接口继承 1 2 3 4 5 6 7 8 9 10 11 interface Shape{     color :string } interface Square extends Shape{     sideLength : number } var  s = <Square > {};s.color = "blue"; s.sideLength = 10; 
 
混合类型1 2 3 4 5 6 7 8 9 10 interface Counter {     interval: number;     reset(): void ;     (start: number): string } let  c:Counter;c.interval = 123 ; c.reset(); c(10 ) 
上面相当于一个函数对象,在上面挂载了自己的方法和属性
3. 继承 继承:可以拓展已有的类。
示例:在table组件中,父类中定义了基本的获取数据等方法,子类中负责自己的渲染逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class  Table  {    constructor (){         this .getData()     }     getData(){         let  that = this ;         $.ajax({             url : url,             success(){                 that.renderTable()             }         })              }     renderTable(){              } } class  myTable  extends  Table  {    constructor (){                  super ()     }          renderTable(){             } } 
 
4. 泛型类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  User  {    public name : string; 	public password : string; } class  GenericRespository <T > {    private _url : string;     constructor (url){         this ._url = url;     }     public getAsync(){         return  new  Promise ((resolve:(entities:T[] )=> void ,reject)=>{             $.ajax({                 url : this ._url,                 success:(data )=> {                     var  list = <T[] > data.items;                     resolve(list)                 }             })         })     } } var userRepository = new GenericRespository<User>("./demo/shared/users.json"); userRepository.then((users:User[])=>{}) 
 
举个后端返回接口的例子
简单的:返回值格式是相通的,但Data是不同的
1 2 3 4 5 6 public class PageList<T>{     public int PageIndex{get;set;} 	public int PageSize{get;set;} 	public int TotalCount{get;set;} 	public List<T> Data{get;set;} } 
 
复杂的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class ExecuteResult{     public int Status{get;set;}     public string Message{get;set;}     //多态     //方法名和类名一样 即为构造函数     public ExecuteResult(int status,string message){         this.Status = status;         this.Message = message;     }     //多态     public ExecuteResult(int status){         this.Status = status;     } }      //继承上面的类,因此有了status和message public class ExecuteResult<T>:ExecuteResult{     //返回值的data部分     public T Data{get;set;}     public ExcuteResult(int status,string message){         this.Status = status;         this.Message = message;     } } public class PageList<T>{     public int PageIndex{get;set;} 	public int PageSize{get;set;} 	public int TotalCount{get;set;} 	public List<T> Data{get;set;} } public class OrderData{     //where是范型约束     //PagedList<T> 因为范型约束为OrderListDTO,所以Data也是OrderListDTO类型     public PagedList<T> GetPagedOrderListDTO<T>(OrderQueryDTO query) where T:OrderListDTO{              }	 } //调用 return new ExcuteResult<PageList<T>>(1,'success') {Data = new OrderData().GetPagedOrderListDTO<T>(query)}	//大括号内容是给Data赋值 
 
5. 泛型约束 5.1 泛型约束是什么 比如在上一章节中,我们想要给实例增加一个isValidate方法,用于验证。但是直接添加这个方法会报错,因为泛型不是具体的类型,不会有isValidate方法的。
1 2 3 4 5 6 7 8 9 10 success:(data )=> {     var  list = <T[] > data.items;     let items : <T[]>     for(var i=0;i<list.length;i++){         if(list[i].isValidate()){	//报错                      }     }     resolve(list) } 
 
正确的方法:添加泛型约束(即用interface给实例类型做个约束)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 interface  ValidatableInterface{    isValidate():boolean  } class  User implements  ValidatableInterface{    public  name : string ; 	public  password : string ;     public  isValidate():boolean {         return  true ;     } } class  GenericRespository<T extends  ValidatableInterface>{    private  _url : string ;     constructor (url ){         this ._url = url;     }     public  getAsync(){         return  new  Promise ((resolve,reject )=> {             $.ajax({                 url : this ._url,                 success:(data )=> {                     var  list = <T[]>data.items;	                     var  items:T[];                     for (var  i=0 ;i<list.length;i++){                         if (list[i].isValidate()){                             items.push(list[i])                         }                     }                     resolve(list)                 }             })         })     } } var  userRepository = new  GenericRespository<User>("./demo/shared/users.json" );userRepository.getAsync().then((users:User[] )=> {}) 
 
6. 命名空间(namespace)(也叫做内部模块) models.ts
1 2 3 4 5 6 7 8 9 10 namespace  app{    export  namespace  models{         export  class  UserModel{         }         export  class  TalkModel{                      }     } } 
 
Main.ts
1 2 3 4 var  user = new  app.models.UserModel();var  talk = new  app.models.TalkModel();
 
关于输出:
运行tsc --output output.js main.ts,会以main.ts为入口,进行打包。打包结果如下: 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var  app;(function  (app )  {     var  models;     (function  (models )  {         var  UserModel =  (function  ( )  {             function  UserModel ( )  {             }             return  UserModel;         }());         models.UserModel = UserModel;         var  TalkModel =  (function  ( )  {             function  TalkModel ( )  {             }             return  TalkModel;         }());         models.TalkModel = TalkModel;     })(models = app.models || (app.models = {})); })(app || (app = {})); var  user = new  app.models.UserModel();var  talk = new  app.models.TalkModel();
 
依次编译,并分别通过script标签引入。 
 
其他问题 一. Typescript中使用async/await 1. 将tsConfig.json中的compilerOptions.lib添加上es2015; 2. 写源代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Test{    constructor ( ){         this .goTest()     }     async  goTest(){         let  aaa = await  this .getData();         console .log(aaa);     }     getData(){         return  new  Promise ((resolve,reject )=> {             setTimeout(()  =>  {                 resolve(222 )             }, 3000 );         })              } } new  Test()
 
3. 编译 4. 添加promise-polyfill(Typescript会把async/await编译成Promise) 二. jQuery拓展插件声明文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 interface MyPlugin {     settings: MyPluginSettings;          (behavior: 'enable' ): JQuery;     (settings?: MyPluginSettings): JQuery; } interface MyPluginSettings {     title?: string; } interface JQueryStatic {     myPlugin: MyPlugin; } $.myPlugin({title :'asd' }) interface JQuery{     cxCalendar():void  } $("#box" ).cxCalendar()